from backend.models.user import User, Role, Permission, Department
from passlib.context import CryptContext
from tortoise.exceptions import DoesNotExist
from typing import Optional, Dict, Any, List, Tuple, Set, Union, cast
from tortoise.queryset import QuerySet
from backend.schemas.user import UserCreate, UserUpdate, RoleCreate, RoleUpdate, PermissionCreate
from tortoise.transactions import atomic
import uuid
import json
from backend.common.cache import get_redis

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class DepartmentService:
    # Redis key for department tree
    DEPARTMENT_TREE_CACHE_KEY = "department:tree"
    
    @staticmethod
    async def create_department(name: str, description: Optional[str] = None, 
                               parent_id: Optional[str] = None, order_num: int = 0):
        """创建部门"""
        # 如果有父部门，需要先获取父部门信息
        parent = None
        if parent_id:
            parent = await DepartmentService.get_department_by_id(parent_id)
            if not parent:
                raise ValueError("指定的父部门不存在")
        
        # 创建部门
        department = await Department.create(
            dept_name=name,
            dept_id=str(uuid.uuid4()).replace("-", "").upper(),  # 生成唯一的部门ID
            description=description,
            parent_id=parent_id,
            order_num=order_num,
            level=parent.level + 1 if parent else 1
        )
        
        # 计算并设置祖先路径
        if parent:
            if parent.ancestors:
                department.ancestors = f"OU={department.dept_name},{parent.ancestors}"
            else:
                department.ancestors = f"OU={department.dept_name}"
        else:
            department.ancestors = f"OU={department.dept_name}"
        
        await department.save()
        
        # 更新部门树缓存
        await DepartmentService.refresh_department_tree_cache()
        
        return department
    
    @staticmethod
    async def get_department_by_id(department_id):
        """通过ID获取部门"""
        try:
            # 先尝试按dept_id字段查询
            try:
                return await Department.get(dept_id=department_id)
            except DoesNotExist:
                # 如果不存在，尝试转换为整数按主键ID查询
                try:
                    dept_pk = int(department_id)
                    return await Department.get(id=dept_pk)
                except (ValueError, DoesNotExist):
                    return None
        except Exception as e:
            import traceback
            traceback.print_exc()
            print(f"获取部门异常: {str(e)}")
            return None
    
    @staticmethod
    async def get_department_by_code(code: str):
        """通过代码获取部门"""
        try:
            return await Department.get(code=code)
        except DoesNotExist:
            return None
    
    @staticmethod
    async def get_departments(page: int = 1, limit: int = 10) -> Tuple[List[Department], int]:
        """获取分页部门列表"""
        skip = (page - 1) * limit
        departments = await Department.all().order_by("level", "order", "id").offset(skip).limit(limit)
        total = await Department.all().count()
        return departments, total
    
    @staticmethod
    async def get_all_departments() -> List[Department]:
        """获取所有部门"""
        return await Department.all().order_by("level", "order", "id")
    
    @staticmethod
    async def get_department_tree() -> List[Dict]:
        """获取部门树结构，优先从Redis缓存获取"""
        try:
            # 尝试从Redis获取部门树
            redis = get_redis()
            cached_tree = await redis.get(DepartmentService.DEPARTMENT_TREE_CACHE_KEY)
            
            # 如果缓存存在，直接返回
            if cached_tree:
                return json.loads(cached_tree)
                
            # 如果缓存不存在，从数据库获取并缓存
            result = await Department.get_tree()
            
            # 将结果缓存到Redis，设置24小时过期时间
            await redis.set(
                DepartmentService.DEPARTMENT_TREE_CACHE_KEY, 
                json.dumps(result),
                ex=86400  # 24小时过期
            )
            
            return result
        except json.JSONDecodeError:
            # 如果JSON解析错误，重新从数据库获取
            try:
                result = await Department.get_tree()
                # 更新缓存
                await redis.set(
                    DepartmentService.DEPARTMENT_TREE_CACHE_KEY, 
                    json.dumps(result),
                    ex=86400
                )
                return result
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise ValueError(f"获取部门树失败: {str(e)}")
        except Exception as e:
            import traceback
            traceback.print_exc()
            # 尝试直接从数据库获取，避免因缓存问题导致功能不可用
            try:
                return await Department.get_tree()
            except:
                raise ValueError(f"获取部门树失败: {str(e)}")
    
    @staticmethod
    async def refresh_department_tree_cache():
        """刷新部门树缓存"""
        try:
            # 从数据库获取最新部门树
            result = await Department.get_tree()
            
            # 更新Redis缓存
            redis = get_redis()
            await redis.set(
                DepartmentService.DEPARTMENT_TREE_CACHE_KEY, 
                json.dumps(result),
                ex=86400  # 24小时过期
            )
            
            return True
        except Exception as e:
            import traceback
            traceback.print_exc()
            return False
    
    @staticmethod
    async def initialize_department_tree_cache():
        """初始化部门树缓存"""
        try:
            await DepartmentService.refresh_department_tree_cache()
            return True
        except Exception as e:
            import traceback
            traceback.print_exc()
            return False
    
    @staticmethod
    async def update_department(department_id: str, department_data: Dict[str, Any]) -> Optional[Department]:
        """更新部门"""
        department = await DepartmentService.get_department_by_id(department_id)
        if not department:
            return None
        
        # 检查parent_id是否更改，如果更改了需要更新level和ancestors
        if "parent_id" in department_data and department_data["parent_id"] != department.parent_id:
            parent_id = department_data["parent_id"]
            
            # 确保不会将部门作为自己的子部门
            if parent_id == department.dept_id:
                raise ValueError("部门不能作为自己的子部门")
                
            # 确保不会将部门移动到其子部门下
            if parent_id:
                parent = await DepartmentService.get_department_by_id(parent_id)
                if not parent:
                    raise ValueError("指定的父部门不存在")
                    
                # 检查是否将部门移动到其子部门下
                children = await department.get_all_children_recursive()
                child_ids = [child.dept_id for child in children]
                if parent_id in child_ids:
                    raise ValueError("不能将部门移动到其子部门下")
                    
                # 更新level - 父部门的level加1
                department_data["level"] = parent.level + 1
                
                # 更新ancestors - 设置为 OU=当前部门,父部门的祖先路径
                if parent.ancestors:
                    department_data["ancestors"] = f"OU={department.dept_name},{parent.ancestors}"
                else:
                    department_data["ancestors"] = f"OU={department.dept_name}"
            else:
                # 变成顶级部门
                department_data["level"] = 1
                department_data["ancestors"] = f"OU={department.dept_name}"
        
        # 保存原始父部门ID，用于后续判断是否需要更新子部门
        old_parent_id = department.parent_id
        old_level = department.level
        old_ancestors = department.ancestors
        
        # 更新部门字段
        for key, value in department_data.items():
            setattr(department, key, value)
            
        await department.save()
        
        # 如果修改了parent_id或level或ancestors，需要递归更新所有子部门的level和ancestors
        if ("parent_id" in department_data and department_data["parent_id"] != old_parent_id) or \
           ("level" in department_data and department_data["level"] != old_level) or \
           ("ancestors" in department_data and department_data["ancestors"] != old_ancestors):
            await DepartmentService._update_children_level_and_ancestors(department)
            
        # 更新部门树缓存
        await DepartmentService.refresh_department_tree_cache()
        
        return department
    
    @staticmethod
    async def _update_children_level_and_ancestors(department: Department):
        """递归更新子部门的level和ancestors"""
        # 获取直接子部门
        children = await department.get_children()
        
        for child in children:
            # 更新子部门的level (parent level + 1)
            child.level = department.level + 1
            
            # 更新子部门的ancestors
            if department.ancestors:
                child.ancestors = f"OU={child.dept_name},{department.ancestors}"
            else:
                child.ancestors = f"OU={child.dept_name}"
            
            # 保存子部门的变更
            await child.save()
            
            # 递归更新该子部门的所有子部门
            await DepartmentService._update_children_level_and_ancestors(child)
    
    @staticmethod
    async def get_or_create_unassigned_department():
        """获取或创建未分配部门"""
        try:
            # 尝试获取未分配部门
            unassigned_dept = await Department.get(dept_name="未分配")
            return unassigned_dept
        except DoesNotExist:
            # 如果不存在则创建
            unassigned_dept = await Department.create(
                dept_name="未分配",
                dept_id="UNASSIGNED",
                description="未分配部门，用于存放没有明确部门归属的员工",
                level=1,
                ancestors="OU=未分配"
            )
            return unassigned_dept
    
    @staticmethod
    async def delete_department(department_id: str) -> bool:
        """删除部门"""
        department = await DepartmentService.get_department_by_id(department_id)
        if not department:
            return False
            
        # 检查是否有子部门
        children = await department.get_children()
        if children:
            raise ValueError("该部门下有子部门，无法直接删除")
            
        # 检查部门是否关联了角色
        # Department doesn't have an explicit id field but tortoise ORM should provide a PK
        # Use dept_id instead which is the business key
        roles_count = await Role.filter(department__dept_id=department.dept_id).count()
        if roles_count > 0:
            raise ValueError("该部门关联了角色，无法直接删除")
            
        # 获取未分配部门
        unassigned_dept = await DepartmentService.get_or_create_unassigned_department()
        
        # 该部门如果是未分配部门，则不允许删除
        if department.dept_id == "UNASSIGNED":
            raise ValueError("未分配部门不能被删除")
            
        # 获取该部门下的所有用户
        # Use dept_id instead of id
        users = await User.filter(department__dept_id=department.dept_id).all()
        
        # 将该部门下的用户迁移到未分配部门
        if users:
            for user in users:
                user.department = unassigned_dept
                await user.save()
            
        # 删除部门
        await department.delete()
        
        # 更新部门树缓存
        await DepartmentService.refresh_department_tree_cache()
        
        return True

    @staticmethod
    async def get_department_permissions(department_id: str) -> List[Permission]:
        """获取部门拥有的权限列表"""
        department = await Department.get_or_none(dept_id=department_id)
        if not department:
            raise ValueError(f"部门ID为 {department_id} 的部门不存在")
            
        await department.fetch_related("permissions")
        return cast(List[Permission], department.permissions)
    
    @staticmethod
    @atomic()
    async def assign_permissions_to_department(department_id: str, permission_ids: List[int]) -> Department:
        """分配权限到部门"""
        department = await Department.get_or_none(dept_id=department_id)
        if not department:
            raise ValueError(f"部门ID为 {department_id} 的部门不存在")
            
        # 检查权限是否存在
        permissions = await Permission.filter(id__in=permission_ids)
        if len(permissions) != len(permission_ids):
            found_ids = [p.id for p in permissions]
            missing_ids = [pid for pid in permission_ids if pid not in found_ids]
            raise ValueError(f"以下权限ID不存在: {missing_ids}")
            
        # 清除现有权限
        await department.permissions.clear()
        
        # 添加新权限
        if permissions:
            await department.permissions.add(*permissions)
            
        # 刷新缓存
        await DepartmentService.refresh_department_tree_cache()
            
        return department
    
    @staticmethod
    @atomic()
    async def update_department_permissions(department_id: str, permission_ids: List[int]) -> Department:
        """更新部门权限（不清除现有权限，只添加新权限）"""
        department = await Department.get_or_none(dept_id=department_id)
        if not department:
            raise ValueError(f"部门ID为 {department_id} 的部门不存在")
            
        # 检查权限是否存在
        permissions = await Permission.filter(id__in=permission_ids)
        if len(permissions) != len(permission_ids):
            found_ids = [p.id for p in permissions]
            missing_ids = [pid for pid in permission_ids if pid not in found_ids]
            raise ValueError(f"以下权限ID不存在: {missing_ids}")
            
        # 添加新权限
        if permissions:
            await department.permissions.add(*permissions)
            
        # 刷新缓存
        await DepartmentService.refresh_department_tree_cache()
            
        return department
    
    @staticmethod
    @atomic()
    async def remove_permissions_from_department(department_id: str, permission_ids: List[int]) -> Department:
        """从部门移除权限"""
        department = await Department.get_or_none(dept_id=department_id)
        if not department:
            raise ValueError(f"部门ID为 {department_id} 的部门不存在")
            
        # 检查权限是否存在
        permissions = await Permission.filter(id__in=permission_ids)
        
        # 移除权限
        for permission in permissions:
            await department.permissions.remove(permission)
            
        # 刷新缓存
        await DepartmentService.refresh_department_tree_cache()
            
        return department

class PermissionService:
    @staticmethod
    async def create_permission(name: str, code: str, description: Optional[str] = None):
        permission = await Permission.create(name=name, code=code, description=description)
        return permission
    
    @staticmethod
    async def get_permission_by_id(permission_id: int):
        try:
            return await Permission.get(id=permission_id)
        except DoesNotExist:
            return None
    
    @staticmethod
    async def get_permissions(page: int = 1, limit: int = 10) -> Tuple[List[Permission], int]:
        """Get paginated permissions"""
        skip = (page - 1) * limit
        permissions = await Permission.all().offset(skip).limit(limit)
        total = await Permission.all().count()
        return permissions, total
    
    @staticmethod
    async def get_all_permissions() -> List[Permission]:
        """Get all permissions"""
        return await Permission.all()
    
    @staticmethod
    async def update_permission(permission_id: int, permission_data: Dict[str, Any]) -> Optional[Permission]:
        """Update permission by id"""
        permission = await PermissionService.get_permission_by_id(permission_id)
        if not permission:
            return None
            
        # Update permission fields
        for key, value in permission_data.items():
            setattr(permission, key, value)
            
        await permission.save()
        return permission
    
    @staticmethod
    async def delete_permission(permission_id: int) -> bool:
        """Delete permission by id"""
        permission = await PermissionService.get_permission_by_id(permission_id)
        if not permission:
            return False
            
        await permission.delete()
        return True

class RoleService:
    @staticmethod
    @atomic()
    async def create_role(name: str, code: str, description: Optional[str] = None, 
                         permission_ids: List[int] = [], department_id: Optional[str] = None):
        # Check if department exists if provided
        if department_id:
            department = await Department.get_or_none(dept_id=department_id)
            if not department:
                raise ValueError(f"部门ID为 {department_id} 的部门不存在")
            
        # Create role
        role = await Role.create(
            name=name,
            code=code,
            description=description,
            department_id=department_id
        )
        
        # Assign permissions
        if permission_ids:
            permissions = await Permission.filter(id__in=permission_ids)
            if permissions:
                await role.permissions.add(*permissions)
        
        return role
    
    @staticmethod
    async def get_role_by_id(role_id: int):
        try:
            return await Role.get(id=role_id).prefetch_related("permissions", "department")
        except DoesNotExist:
            return None
    
    @staticmethod
    async def get_role_by_code(code: str):
        try:
            return await Role.get(code=code).prefetch_related("permissions")
        except DoesNotExist:
            return None
    
    @staticmethod
    async def get_roles(page: int = 1, limit: int = 10, department_id: Optional[str] = None) -> Tuple[List[Role], int]:
        """获取分页角色列表，可按部门筛选"""
        skip = (page - 1) * limit
        
        # 构建查询条件
        query = Role.all().prefetch_related('permissions')
        
        # 如果指定了部门，按部门筛选
        if department_id:
            department = await Department.get_or_none(dept_id=department_id)
            if department:
                query = query.filter(department=department)
                
        # 执行查询
        roles = await query.offset(skip).limit(limit)
        total = await query.count()
        
        return roles, total
    
    @staticmethod
    async def get_all_roles(department_id: Optional[str] = None) -> List[Role]:
        """获取所有角色，可按部门筛选"""
        # 构建查询条件
        query = Role.all().prefetch_related('permissions')
        
        # 如果指定了部门，按部门筛选
        if department_id:
            department = await Department.get_or_none(dept_id=department_id)
            if department:
                query = query.filter(department=department)
                
        return await query
    
    @staticmethod
    @atomic()
    async def update_role(role_id: int, role_data: Dict[str, Any]) -> Optional[Role]:
        """更新角色"""
        role = await RoleService.get_role_by_id(role_id)
        if not role:
            return None
            
        # 检查name和code的唯一性
        if "name" in role_data and role_data["name"] != role.name:
            existing_role = await Role.get_or_none(name=role_data["name"])
            if existing_role:
                raise ValueError(f"角色名 '{role_data['name']}' 已被使用")
                
        if "code" in role_data and role_data["code"] != role.code:
            existing_role = await Role.get_or_none(code=role_data["code"])
            if existing_role:
                raise ValueError(f"角色代码 '{role_data['code']}' 已被使用")
                
        # 检查部门是否存在
        if "department_id" in role_data and role_data["department_id"]:
            department = await Department.get_or_none(dept_id=role_data["department_id"])
            if not department:
                raise ValueError(f"部门ID为 {role_data['department_id']} 的部门不存在")
                
        # 更新角色权限
        if "permission_ids" in role_data:
            # 清除现有权限
            await role.permissions.clear()
            
            # 添加新权限
            permission_ids = role_data.pop("permission_ids")
            if permission_ids:
                permissions = await Permission.filter(id__in=permission_ids)
                if permissions:
                    await role.permissions.add(*permissions)
        
        # 更新角色字段
        for key, value in role_data.items():
            setattr(role, key, value)
            
        await role.save()
        return role
    
    @staticmethod
    async def delete_role(role_id: int) -> bool:
        """Delete role by id"""
        role = await RoleService.get_role_by_id(role_id)
        if not role:
            return False
            
        await role.delete()
        return True

class UserService:
    @staticmethod
    @atomic()
    async def create_user(username: str, password: str, **kwargs):
        hashed_password = pwd_context.hash(password)
        
        # Extract role_ids if present
        role_ids = kwargs.pop("role_ids", [])
        
        # Check if department exists if provided
        department_id = kwargs.pop("department_id", None)
        department = None
        if department_id:
            department = await Department.get_or_none(dept_id=department_id)
            if not department:
                raise ValueError("指定的部门不存在")
            kwargs["department"] = department
        
        user = await User.create(username=username, hashed_password=hashed_password, **kwargs)
        
        # Assign roles if specified
        for role_id in role_ids:
            role = await Role.get_or_none(id=role_id)
            if role:
                await user.roles.add(role)
                
        return user

    @staticmethod
    async def get_user_by_username(username: str):
        try:
            return await User.get(username=username).prefetch_related("roles__permissions")
        except DoesNotExist:
            return None

    @staticmethod
    async def get_user_by_id(user_id: int):
        try:
            return await User.get(id=user_id).prefetch_related("roles__permissions", "department")
        except DoesNotExist:
            return None

    @staticmethod
    def verify_password(plain_password: str, hashed_password: str) -> bool:
        return pwd_context.verify(plain_password, hashed_password)
        
    @staticmethod
    async def authenticate_user(username: str, password: str):
        user = await UserService.get_user_by_username(username)
        if not user:
            return None
        if not UserService.verify_password(password, user.hashed_password):
            return None
        return user

    @staticmethod
    async def get_users(page: int = 1, limit: int = 10) -> Tuple[List[User], int]:
        """Get paginated users"""
        skip = (page - 1) * limit
        users = await User.all().prefetch_related("roles__permissions", "department").offset(skip).limit(limit)
        total = await User.all().count()
        return users, total
        
    @staticmethod
    async def get_users_by_department(department_id: str, page: int = 1, limit: int = 10) -> Tuple[List[User], int]:
        """获取指定部门的用户列表"""
        skip = (page - 1) * limit
        
        department = await Department.get_or_none(dept_id=department_id)
        if not department:
            return [], 0
            
        users = await User.filter(department=department).prefetch_related('roles', 'roles__permissions', 'department').offset(skip).limit(limit)
        total = await User.filter(department=department).count()
        
        return users, total

    @staticmethod
    @atomic()
    async def update_user(user_id: int, user_data: Dict[str, Any]) -> Optional[User]:
        """Update user by id"""
        user = await UserService.get_user_by_id(user_id)
        if not user:
            return None
        
        # Extract role_ids and department_id if present
        role_ids = user_data.pop("role_ids", None)
        department_id = user_data.pop("department_id", None)
        
        # Update user fields
        for key, value in user_data.items():
            setattr(user, key, value)
            
        await user.save()
        
        # Update roles if specified
        if role_ids is not None:
            # Clear current roles
            await user.roles.clear()
            
            # Add new roles
            for role_id in role_ids:
                role = await Role.get_or_none(id=role_id)
                if role:
                    await user.roles.add(role)
        
        # Update department if specified
        if department_id is not None:
            if department_id:
                department = await Department.get_or_none(dept_id=department_id)
                if department:
                    user.department = department
                    await user.save()
            else:
                # If department_id is empty string or None, set department to None
                user.department = None
                await user.save()
        
        # Reload user with relationships
        return await UserService.get_user_by_id(user_id)
        
    @staticmethod
    async def delete_user(user_id: int) -> bool:
        """Delete user by id"""
        user = await UserService.get_user_by_id(user_id)
        if not user:
            return False
            
        await user.delete()
        return True
        
    @staticmethod
    async def change_password(user_id: int, old_password: str, new_password: str) -> bool:
        """Change user password"""
        user = await UserService.get_user_by_id(user_id)
        if not user:
            return False
            
        if not UserService.verify_password(old_password, user.hashed_password):
            return False
            
        user.hashed_password = pwd_context.hash(new_password)
        await user.save()
        return True
    
    @staticmethod
    async def get_user_permissions(user: User) -> Set[str]:
        """
        获取用户所有权限，权限来源包括：
        1. 用户直接关联的角色权限
        2. 用户所属部门关联的角色权限
        3. 用户所属部门的所有上级部门关联的角色权限（部门权限自上而下继承）
        
        权限继承规则：
        - 超级管理员拥有所有系统权限
        - 普通用户从其关联的角色获取权限
        - 用户额外继承其所属部门关联的所有角色权限
        - 用户额外继承其所属部门的上级部门（父部门、祖父部门等）关联的所有角色权限
        
        Args:
            user: User对象
            
        Returns:
            包含所有权限代码的集合
        """
        if not user:
            return set()
        
        # For superuser, return all permissions
        if user.is_superuser:
            permissions = await Permission.all()
            return {perm.code for perm in permissions}
        
        # Ensure roles are loaded
        if not hasattr(user, "roles") or not user.roles:
            await user.fetch_related("roles__permissions")
        
        # Collect permissions from all roles
        permission_codes = set()
        for role in user.roles:
            if not hasattr(role, "permissions") or not role.permissions:
                await role.fetch_related("permissions")
            for permission in role.permissions:
                permission_codes.add(permission.code)
        
        # 获取用户所属部门及其祖先部门的角色权限
        await user.fetch_related("department")
        if user.department:
            # 确保department是一个Department实例
            user_department = user.department
            
            # 获取用户部门关联的角色权限
            await UserService._collect_department_permissions(user_department, permission_codes)
            
            # 处理父部门权限
            if user_department.parent_id:
                parent_dept = await Department.get_or_none(dept_id=user_department.parent_id)
                if parent_dept:
                    # 获取父部门关联的角色权限
                    await UserService._collect_department_permissions(parent_dept, permission_codes)
                    
                    # 递归获取更高层级的祖先部门权限
                    current_dept = parent_dept
                    while current_dept.parent_id:
                        parent = await Department.get_or_none(dept_id=current_dept.parent_id)
                        if parent:
                            await UserService._collect_department_permissions(parent, permission_codes)
                            current_dept = parent
                        else:
                            break
        
        return permission_codes
        
    @staticmethod
    async def _collect_department_permissions(department: Department, permission_codes: Set[str]):
        """从指定部门收集角色权限和直接关联的权限"""
        if department is None:
            return
            
        # Ensure department is a Department instance, not a QuerySet
        if not isinstance(department, Department):
            # If it's just an ID
            if isinstance(department, (int, str)):
                department = await Department.get_or_none(dept_id=department)
                if not department:
                    return
            else:
                # If it's a QuerySet or something else, try to get the actual Department
                return
                
        # 获取部门关联的角色
        department_roles = await Role.filter(department=department).prefetch_related("permissions")
                
        # 添加部门角色的权限
        for role in department_roles:
            for permission in role.permissions:
                permission_codes.add(permission.code)
                
        # 添加部门直接关联的权限
        await department.fetch_related("permissions")
        for permission in department.permissions:
            permission_codes.add(permission.code)
    
    @staticmethod
    async def has_permission(user: User, permission_code: str) -> bool:
        """Check if user has a specific permission"""
        user_permissions = await UserService.get_user_permissions(user)
        return permission_code in user_permissions 